/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-01-14     马里亚纳砖头 first version
 * 2022-01-23     chenbin      add
 */

#include <board.h>
#include "drv_sdio.h"

#ifdef BSP_USING_SDIO

//#define DRV_DEBUG
#define LOG_TAG             "drv.sdio"
#include <drv_log.h>

#include "n32g45x.h"
#include "n32g45x_sdio.h"

#define SDIO_FIFO_ADDRESS ((uint32_t)0x40018080) // SDIO_FIOF_ADDR=SDIO_base+0x80->sdio_base+0xfc

#define SDIO_BUFF_SIZE       4096
#define SDIO_ALIGN_LEN       32

struct sdio_pkg
{
    struct rt_mmcsd_cmd *cmd;
    void *buff;
    rt_uint32_t flag;
};

struct rt_hw_sdio_n32
{
    struct rt_mmcsd_host         *host;
    struct rt_event              event;
    struct rt_mutex              mutex;
	
    struct sdio_pkg 						*pkg;
		
		SDIO_Module*         Instance;
		SDIO_InitType        SDIO_InitStructure;
		SDIO_CmdInitType     SDIO_CmdInitStructure;
		SDIO_DataInitType    SDIO_DataInitStructure;
};

static struct rt_hw_sdio_n32   n32_sdio1 = {0};

ALIGN(SDIO_ALIGN_LEN)
static rt_uint8_t cache_buff[SDIO_BUFF_SIZE];

#define N32_SDIO_ERRORS_FLAG  (SDIO_INT_CCRCERR | SDIO_INT_DCRCERR | \
															SDIO_INT_CMDTIMEOUT | SDIO_INT_DATTIMEOUT | \
															SDIO_INT_TXURERR | SDIO_INT_RXORERR )

#define N32_SDIO_LOCK(_n32_sdio)    rt_mutex_take(&_n32_sdio->mutex, RT_WAITING_FOREVER);
#define N32_SDIO_UNLOCK(_n32_sdio)  rt_mutex_release(&_n32_sdio->mutex);


/*!< Power ON Sequence -----------------------------------------------------*/
/*!< Configure the SDIO peripheral */
/*!< SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + SDIO_INIT_CLK_DIV) */
/*!< on N32G45X devices, SDIOCLK is fixed to 48MHz */
/*!< SDIO_CK for initialization should not exceed 400 KHz */
/*The clock at initialization cannot be greater than 400kHz*/
/*8M:18   18M:45 */
/* HCLK = 72MHz, SDIOCLK = 72MHz, SDIO_CK = HCLK/(178 + 2) = 400 KHz */
static uint32_t n32_get_clk_div(uint32_t clk_set)
{
	uint32_t clk_src = (72U*1000U*1000U);
	uint32_t clk_div = 0;
	if(clk_set == 0)
	{
		clk_div = 2;
	}
	else
	{
		clk_div = clk_src / clk_set;
		if (clk_div < 2)
		{
				clk_div = 2;
		}
		else if (clk_div > 0x3FF)
		{
				clk_div = 0x3FF;
		}
		clk_div -= 2;
	}
	//clk_div = 178;
	return clk_div;
}

void SD_DMA_RxConfig(uint32_t* BufferDST, uint32_t BufferSize)
{
    DMA_InitType DMA_InitStructure;

    DMA_ClearFlag(DMA2_FLAG_TC4 | DMA2_FLAG_TE4 | DMA2_FLAG_HT4 | DMA2_FLAG_GL4, DMA2); // Clear DMA flag bit

    /*!< DMA2 Channel4 disable */
    DMA_EnableChannel(DMA2_CH4, DISABLE); // SDIO is the fourth channel

    /*!< DMA2 Channel4 Config */
    DMA_InitStructure.PeriphAddr = (uint32_t)SDIO_FIFO_ADDRESS; // Peripheral address,fifo
    DMA_InitStructure.MemAddr    = (uint32_t)BufferDST;         // Destination address
    DMA_InitStructure.Direction  = DMA_DIR_PERIPH_SRC;          // Peripheral as original address
    DMA_InitStructure.BufSize    = BufferSize / 4;              // 1/4 cache size
    DMA_InitStructure.PeriphInc  = DMA_PERIPH_INC_DISABLE; // Enable peripheral address not to increase automatically
    DMA_InitStructure.DMA_MemoryInc = DMA_MEM_INC_ENABLE;  // Enable storage target address auto increment
    DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_WORD; // Peripheral data size is word, 32-bit
    DMA_InitStructure.MemDataSize = DMA_MemoryDataSize_Word;
    DMA_InitStructure.CircularMode = DMA_MODE_NORMAL; // No loop, loop mode is mainly used on ADC
    DMA_InitStructure.Priority = DMA_PRIORITY_HIGH;   // High channel priority
    DMA_InitStructure.Mem2Mem  = DMA_M2M_DISABLE;     // Non memory to memory mode
    DMA_Init(DMA2_CH4, &DMA_InitStructure);

    /*!< DMA2 Channel4 enable */ // Do not set DMA interrupt
    DMA_EnableChannel(DMA2_CH4, ENABLE);
}

void SD_DMA_TxConfig(uint32_t* BufferSRC, uint32_t BufferSize)
{
    DMA_InitType DMA_InitStructure;
    DMA_ClearFlag(DMA2_FLAG_TC4 | DMA2_FLAG_TE4 | DMA2_FLAG_HT4 | DMA2_FLAG_GL4, DMA2);

    /*!< DMA2 Channel4 disable */
    DMA_EnableChannel(DMA2_CH4, DISABLE);

    /*!< DMA2 Channel4 Config */
    DMA_InitStructure.PeriphAddr     = (uint32_t)SDIO_FIFO_ADDRESS;
    DMA_InitStructure.MemAddr        = (uint32_t)BufferSRC;
    DMA_InitStructure.Direction      = DMA_DIR_PERIPH_DST; // Peripheral as write target
    DMA_InitStructure.BufSize        = BufferSize / 4;
    DMA_InitStructure.PeriphInc      = DMA_PERIPH_INC_DISABLE; // Peripheral address does not increase automatically
    DMA_InitStructure.DMA_MemoryInc  = DMA_MEM_INC_ENABLE;
    DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_WORD;
    DMA_InitStructure.MemDataSize    = DMA_MemoryDataSize_Word;
    DMA_InitStructure.CircularMode   = DMA_MODE_NORMAL;
    DMA_InitStructure.Priority       = DMA_PRIORITY_HIGH;
    DMA_InitStructure.Mem2Mem        = DMA_M2M_DISABLE;
    DMA_Init(DMA2_CH4, &DMA_InitStructure);

    /*!< DMA2 Channel4 enable */
    DMA_EnableChannel(DMA2_CH4, ENABLE);
}

static int get_block_size_order(rt_uint32_t data)
{
    int order = 0;

    switch (data)
    {
    case 1:
        order = 0;
        break;
    case 2:
        order = 1;
        break;
    case 4:
        order = 2;
        break;
    case 8:
        order = 3;
        break;
    case 16:
        order = 4;
        break;
    case 32:
        order = 5;
        break;
    case 64:
        order = 6;
        break;
    case 128:
        order = 7;
        break;
    case 256:
        order = 8;
        break;
    case 512:
        order = 9;
        break;
    case 1024:
        order = 10;
        break;
    case 2048:
        order = 11;
        break;
    case 4096:
        order = 12;
        break;
    case 8192:
        order = 13;
        break;
    case 16384:
        order = 14;
        break;
    default :
        order = 0;
        break;
    }

    return order;
}

static void n32_sdio_wait_completed(struct rt_hw_sdio_n32 *n32_sdio)
{
    rt_uint32_t status;
    struct rt_mmcsd_cmd *cmd = n32_sdio->pkg->cmd;
    struct rt_mmcsd_data *data = cmd->data;

    if (rt_event_recv(&n32_sdio->event, 0xffffffff, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
                      rt_tick_from_millisecond(5000), &status) != RT_EOK)
    {
        LOG_E("wait completed timeout");
        cmd->err = -RT_ETIMEOUT;
        return;
    }

    if (n32_sdio->pkg == RT_NULL)
    {
        return;
    }

    cmd->resp[0] = n32_sdio->Instance->RESPONSE1; //->resp1;
    cmd->resp[1] = n32_sdio->Instance->RESPONSE2; //->resp2;
    cmd->resp[2] = n32_sdio->Instance->RESPONSE3; //->resp3;
    cmd->resp[3] = n32_sdio->Instance->RESPONSE4; //->resp4;

    if (status & N32_SDIO_ERRORS_FLAG)
    {
        if ((status & SDIO_INT_CCRCERR) && (resp_type(cmd) & (RESP_R3 | RESP_R4)))
        {
            cmd->err = RT_EOK;
        }
        else
        {
            cmd->err = -RT_ERROR;
        }

        if (status & SDIO_INT_CMDTIMEOUT)
        {
            cmd->err = -RT_ETIMEOUT;
        }

        if (status & SDIO_INT_DCRCERR)
        {
            data->err = -RT_ERROR;
        }

        if (status & SDIO_INT_DATTIMEOUT)
        {
            data->err = -RT_ETIMEOUT;
        }

        if (cmd->err == RT_EOK)
        {
            LOG_I("sta:0x%08X [%08X %08X %08X %08X]", status, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
        }
        else
        {
            LOG_W("err:0x%08x, %s%s%s%s%s%s%s cmd:%d arg:0x%08x rw:%c len:%d blksize:%d",
                  status,
                  status & SDIO_INT_CCRCERR  ? "CCRCFAIL "    : "",
                  status & SDIO_INT_DCRCERR  ? "DCRCFAIL "    : "",
                  status & SDIO_INT_CMDTIMEOUT  ? "CTIMEOUT "    : "",
                  status & SDIO_INT_DATTIMEOUT  ? "DTIMEOUT "    : "",
                  status & SDIO_INT_TXURERR  ? "TXUNDERR "    : "",
                  status & SDIO_INT_RXORERR   ? "RXOVERR "     : "",
                  status == 0                   ? "NULL"         : "",
                  cmd->cmd_code,
                  cmd->arg,
                  data ? (data->flags & DATA_DIR_WRITE ?  'w' : 'r') : '-',
                  data ? data->blks * data->blksize : 0,
                  data ? data->blksize : 0
                 );
        }
    }
    else
    {
        cmd->err = RT_EOK;
        LOG_D("sta:0x%08X [%08X %08X %08X %08X]", status, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
    }
}

//static void n32_sdio_transfer_by_dma(struct rt_hw_sdio_n32 *n32_sdio, struct sdio_pkg *pkg)
//{
//    struct rt_mmcsd_data *data;
//    int size;
//    void *buff;

//    if ((RT_NULL == pkg) || (RT_NULL == n32_sdio))
//    {
//        LOG_E("rt_hw_sdio_transfer_by_dma invalid args");
//        return;
//    }

//    data = pkg->cmd->data;
//    if (RT_NULL == data)
//    {
//        LOG_E("rt_hw_sdio_transfer_by_dma invalid args");
//        return;
//    }

//    buff = pkg->buff;
//    if (RT_NULL == buff)
//    {
//        LOG_E("rt_hw_sdio_transfer_by_dma invalid args");
//        return;
//    }
//    size = data->blks * data->blksize;

//    if (data->flags & DATA_DIR_WRITE)
//    {
//			SDIO_ConfigInt(SDIO_INT_DATEND, ENABLE);
//			SDIO_DMACmd(ENABLE);
//			SD_DMA_TxConfig((uint32_t*)buff, size);
//    }
//    else if (data->flags & DATA_DIR_READ)
//    {
//			SDIO_ConfigInt(SDIO_INT_DATEND, ENABLE); // Start data transmission end interrupt
//			SDIO_DMACmd(ENABLE);
//			SD_DMA_RxConfig((uint32_t*)buff, size); // config DMA
//    }
//}

static void n32_sdio_send_command(struct rt_hw_sdio_n32 *n32_sdio, struct sdio_pkg *pkg)
{
    struct rt_mmcsd_cmd *cmd = pkg->cmd;
    struct rt_mmcsd_data *data = cmd->data;
    rt_uint32_t reg_cmd;

    /* save pkg */
    n32_sdio->pkg = pkg;

    LOG_I("CMD:%d ARG:0x%08x RES:%s%s%s%s%s%s%s%s%s rw:%c len:%d blksize:%d",
          cmd->cmd_code,
          cmd->arg,
          resp_type(cmd) == RESP_NONE ? "NONE"  : "",
          resp_type(cmd) == RESP_R1  ? "R1"  : "",
          resp_type(cmd) == RESP_R1B ? "R1B"  : "",
          resp_type(cmd) == RESP_R2  ? "R2"  : "",
          resp_type(cmd) == RESP_R3  ? "R3"  : "",
          resp_type(cmd) == RESP_R4  ? "R4"  : "",
          resp_type(cmd) == RESP_R5  ? "R5"  : "",
          resp_type(cmd) == RESP_R6  ? "R6"  : "",
          resp_type(cmd) == RESP_R7  ? "R7"  : "",
          data ? (data->flags & DATA_DIR_WRITE ?  'w' : 'r') : '-',
          data ? data->blks * data->blksize : 0,
          data ? data->blksize : 0
         );

    /* config data reg */
    if (data != RT_NULL)
    {
			n32_sdio->SDIO_DataInitStructure.DatTimeout        = 0xFFFFFFFF;
			n32_sdio->SDIO_DataInitStructure.DatLen            = data->blks * data->blksize;
			n32_sdio->SDIO_DataInitStructure.DatBlkSize        = get_block_size_order(data->blksize);
			if(data->flags & DATA_DIR_READ)
			{
				n32_sdio->SDIO_DataInitStructure.TransferDirection = SDIO_TRANSDIR_TOSDIO;  //to HOST
			}else
			{
				n32_sdio->SDIO_DataInitStructure.TransferDirection = SDIO_TRANSDIR_TOCARD;  //to DEVICE
			}
			if(data->flags & DATA_STREAM)
			{
				n32_sdio->SDIO_DataInitStructure.TransferMode      = SDIO_TRANSMODE_STREAM;
			}else
			{
				n32_sdio->SDIO_DataInitStructure.TransferMode      = SDIO_TRANSMODE_BLOCK;
			}
			n32_sdio->SDIO_DataInitStructure.DPSMConfig        = SDIO_DPSM_ENABLE;

			SDIO_ConfigData(&(n32_sdio->SDIO_DataInitStructure));
    }

    /* transfer config */
    if (data != RT_NULL)
    {
			if (data->flags & DATA_DIR_WRITE)
			{
				SDIO_ConfigInt(SDIO_INT_DATEND, ENABLE);
				SDIO_DMACmd(ENABLE);
				SD_DMA_TxConfig((uint32_t*)pkg->buff, data->blks * data->blksize);
			}
			else if (data->flags & DATA_DIR_READ)
			{
				SDIO_ConfigInt(SDIO_INT_DATEND, ENABLE); // Start data transmission end interrupt
				SDIO_DMACmd(ENABLE);
				SD_DMA_RxConfig((uint32_t*)pkg->buff, data->blks * data->blksize); // config DMA
			}
    }
    /* open irq */
		SDIO_ConfigInt(N32_SDIO_ERRORS_FLAG , ENABLE);
		SDIO_ConfigInt(SDIO_INT_CMDRESPRECV, ENABLE);
		SDIO_ConfigInt(SDIO_INT_CMDSEND, ENABLE);
		if (data != RT_NULL)
		{
			SDIO_ConfigInt(SDIO_INT_DATEND, ENABLE);
		}
		
    /* config cmd reg */
    if (resp_type(cmd) == RESP_NONE)
		{
			n32_sdio->SDIO_CmdInitStructure.ResponseType = SDIO_RESP_NO;
		}
    else if (resp_type(cmd) == RESP_R2)
		{
			n32_sdio->SDIO_CmdInitStructure.ResponseType = SDIO_RESP_LONG;
		}
		else
		{
			n32_sdio->SDIO_CmdInitStructure.ResponseType = SDIO_RESP_SHORT;
		}
		n32_sdio->SDIO_CmdInitStructure.WaitType    = SDIO_WAIT_NO;
		n32_sdio->SDIO_CmdInitStructure.CPSMConfig    = SDIO_CPSM_ENABLE;
		n32_sdio->SDIO_CmdInitStructure.CmdArgument   = cmd->arg;
		n32_sdio->SDIO_CmdInitStructure.CmdIndex      = cmd->cmd_code;
		
    /* send cmd */
		SDIO_SendCmd(&(n32_sdio->SDIO_CmdInitStructure));
		
    /* wait completed */
    n32_sdio_wait_completed(n32_sdio);

    /* Waiting for data to be sent to completion */
    if (data != RT_NULL)
    {
        volatile rt_uint32_t count = 100000U;
				
				while(count && SDIO_GetFlag(SDIO_FLAG_TXRUN | SDIO_FLAG_RXRUN) )
        {
            count--;
        }

        if ((count == 0) || (SDIO_GetFlag(N32_SDIO_ERRORS_FLAG)))
        {
            cmd->err = -RT_ERROR;
        }
    }

    /* close irq, keep sdio irq */
		SDIO_ConfigInt(N32_SDIO_ERRORS_FLAG , DISABLE);
		SDIO_ConfigInt(SDIO_INT_CMDRESPRECV, DISABLE);
		SDIO_ConfigInt(SDIO_INT_CMDSEND, DISABLE);
		if (data != RT_NULL)
		{
			SDIO_ConfigInt(SDIO_INT_DATEND, DISABLE);
		}
		SDIO_ConfigInt(SDIO_INT_SDIOINT , ENABLE);
		
    /* clear pkg */
    n32_sdio->pkg = RT_NULL;
}


static void n32_sdio_ops_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req)
{
	struct rt_hw_sdio_n32 *n32_sdio = host->private_data;
	struct sdio_pkg pkg;
	struct rt_mmcsd_data *data;
	
	N32_SDIO_LOCK(n32_sdio);
	if (req->cmd != RT_NULL)
	{
			rt_memset(&pkg, 0, sizeof(pkg));
			data = req->cmd->data;
			pkg.cmd = req->cmd;

			if (data != RT_NULL)
			{
					rt_uint32_t size = data->blks * data->blksize;

					RT_ASSERT(size <= SDIO_BUFF_SIZE);

					//pkg.buff = data->buf;
					if ((rt_uint32_t)data->buf & (SDIO_ALIGN_LEN - 1))
					{
							pkg.buff = cache_buff;
							if (data->flags & DATA_DIR_WRITE)
							{
									rt_memcpy(cache_buff, data->buf, size);
							}
					}
			}

			n32_sdio_send_command(n32_sdio, &pkg);

			if ((data != RT_NULL) && (data->flags & DATA_DIR_READ) && ((rt_uint32_t)data->buf & (SDIO_ALIGN_LEN - 1)))
			{
					rt_memcpy(data->buf, cache_buff, data->blksize * data->blks);
			}
	}
	if (req->stop != RT_NULL)
	{
			rt_memset(&pkg, 0, sizeof(pkg));
			pkg.cmd = req->stop;
			n32_sdio_send_command(n32_sdio, &pkg);
	}
	N32_SDIO_UNLOCK(n32_sdio);

	mmcsd_req_complete(n32_sdio->host);
}



static void n32_sdio_ops_set_iocfg(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg)
{
	struct rt_hw_sdio_n32 *n32_sdio = host->private_data;
	uint32_t clk_set = io_cfg->clock;
	
	if (clk_set > host->freq_max)
	{
		LOG_W("clk:%d > freq_max:%d", clk_set, host->freq_max);
		clk_set = host->freq_max;
	}
	if (clk_set < host->freq_min)
	{
		LOG_W("clk:%d < freq_min:%d", clk_set, host->freq_min);
		clk_set = host->freq_min;
	}
	LOG_I("clk:%d width:%s%s%s power:%s%s%s",
				clk_set,
				io_cfg->bus_width == MMCSD_BUS_WIDTH_8 ? "8" : "",
				io_cfg->bus_width == MMCSD_BUS_WIDTH_4 ? "4" : "",
				io_cfg->bus_width == MMCSD_BUS_WIDTH_1 ? "1" : "",
				io_cfg->power_mode == MMCSD_POWER_OFF ? "OFF" : "",
				io_cfg->power_mode == MMCSD_POWER_UP ? "UP" : "",
				io_cfg->power_mode == MMCSD_POWER_ON ? "ON" : "");
	
	N32_SDIO_LOCK(n32_sdio);

	if (io_cfg->bus_width == MMCSD_BUS_WIDTH_8)
	{
		n32_sdio->SDIO_InitStructure.BusWidth = SDIO_BUSWIDTH_8B;
	}
	else if (io_cfg->bus_width == MMCSD_BUS_WIDTH_4)
	{
		n32_sdio->SDIO_InitStructure.BusWidth = SDIO_BUSWIDTH_4B;
	}
	else
	{
		n32_sdio->SDIO_InitStructure.BusWidth = SDIO_BUSWIDTH_1B;
	}
	n32_sdio->SDIO_InitStructure.ClkDiv = n32_get_clk_div(clk_set); // SDIO_INIT_CLK_DIV;

	SDIO_Init(&(n32_sdio->SDIO_InitStructure));
	
	switch (io_cfg->power_mode)
	{
	case MMCSD_POWER_OFF:
			SDIO_SetPower(SDIO_POWER_CTRL_OFF);
			break;
	case MMCSD_POWER_UP:
			SDIO_SetPower(SDIO_POWER_CTRL_ON);
			break;
	case MMCSD_POWER_ON:
			SDIO_SetPower(SDIO_POWER_CTRL_ON);
			break;
	default:
			LOG_W("unknown power_mode %d", io_cfg->power_mode);
			break;
	}
	N32_SDIO_UNLOCK(n32_sdio);
}

static rt_int32_t n32_sdio_ops_get_card_status(struct rt_mmcsd_host *host)
{
    LOG_I("try to detect device");
    return 0x01;
}

void n32_sdio_ops_enable_sdio_irq(struct rt_mmcsd_host *host, rt_int32_t enable)
{
	struct rt_hw_sdio_n32 *n32_sdio = host->private_data;

	if (enable)
	{
		LOG_I("enable sdio irq");
		SDIO_ConfigInt(SDIO_INT_SDIOINT, ENABLE);
	}
	else
	{
		LOG_I("disable sdio irq");
		SDIO_ConfigInt(SDIO_INT_SDIOINT, DISABLE);
	}
}

void n32_sdio_irq_process(struct rt_mmcsd_host *host)
{
	struct rt_hw_sdio_n32 *n32_sdio = host->private_data;
  rt_uint32_t intstatus = n32_sdio->Instance->STS;
	int complete = 0;
	if (intstatus & N32_SDIO_ERRORS_FLAG)
	{
		SDIO_ClrFlag(N32_SDIO_ERRORS_FLAG);
		complete = 1;
	}
	else
	{
		if (intstatus & SDIO_FLAG_CMDRESPRECV)
		{
			SDIO_ClrFlag(SDIO_FLAG_CMDRESPRECV);
			if (n32_sdio->pkg != RT_NULL)
			{
				if (!n32_sdio->pkg->cmd->data)
				{
						complete = 1;
				}
				else if ((n32_sdio->pkg->cmd->data->flags & DATA_DIR_WRITE))
				{
						//hw_sdio->dctrl |= HW_SDIO_DPSM_ENABLE;
				}
			}
		}
		if (intstatus & SDIO_FLAG_CMDSEND)
		{
			SDIO_ClrFlag(SDIO_FLAG_CMDSEND);
			if (resp_type(n32_sdio->pkg->cmd) == RESP_NONE)
			{
					complete = 1;
			}
		}
		if (intstatus & SDIO_FLAG_DATEND)
		{
			SDIO_ClrFlag(SDIO_FLAG_DATEND);
			complete = 1;
		}
	}
	if ((intstatus & SDIO_FLAG_SDIOINT) )
	{
			SDIO_ClrFlag(SDIO_FLAG_SDIOINT);
			sdio_irq_wakeup(host);
	}
	if (complete)
	{
		SDIO_ConfigInt(N32_SDIO_ERRORS_FLAG , DISABLE);
		rt_event_send(&(n32_sdio->event), intstatus);
	}
}

static const struct rt_mmcsd_host_ops n32_sdio_ops =
{
    n32_sdio_ops_request,
    n32_sdio_ops_set_iocfg,
    n32_sdio_ops_get_card_status,
    n32_sdio_ops_enable_sdio_irq,
};

struct rt_mmcsd_host *n32_sdio_host_create(struct rt_hw_sdio_n32 * n32_sdio)
{
	struct rt_mmcsd_host *host = NULL;
	
	if(n32_sdio == NULL)
	{
		LOG_E("n32_sdio is NULL");
		return NULL;
	}
	
	if(n32_sdio->host != NULL)
	{
		return n32_sdio->host;
	}
	
	host = mmcsd_alloc_host();
	if (host == RT_NULL)
	{
			LOG_E("mmcsd alloc host fail");
			return RT_NULL;
	}
	/* set host defautl attributes */
	host->ops = &n32_sdio_ops;
	host->freq_min = 400 * 1000;   //400Khz
	host->freq_max = 1 * 1000 * 1000;  //1Mhz
	host->valid_ocr = 0X00FFFF80;/* The voltage range supported is 1.65v-3.6v */
#ifndef SDIO_USING_1_BIT
	host->flags = MMCSD_BUSWIDTH_4 | MMCSD_MUTBLKWRITE | MMCSD_SUP_SDIO_IRQ;
#else
	host->flags = MMCSD_MUTBLKWRITE | MMCSD_SUP_SDIO_IRQ;
#endif
	host->max_seg_size = 4096;
	host->max_dma_segs = 1;
	host->max_blk_size = 512;
	host->max_blk_count = 512;

	/* link up host and sdio */
	host->private_data = n32_sdio;
	
	rt_event_init(&n32_sdio->event, "sdio", RT_IPC_FLAG_FIFO);
	rt_mutex_init(&n32_sdio->mutex, "sdio", RT_IPC_FLAG_FIFO);
	
	n32_sdio->host = host;
	
	return n32_sdio->host;
}


void SDIO_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();
    /* Process All SDIO Interrupt Sources */
    n32_sdio_irq_process(n32_sdio1.host);

    /* leave interrupt */
    rt_interrupt_leave();
}

void n32_sdio_hw_init(struct rt_hw_sdio_n32 * n32_sdio)
{
  RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA | RCC_APB2_PERIPH_GPIOB| RCC_APB2_PERIPH_GPIOC| RCC_APB2_PERIPH_GPIOD| RCC_APB2_PERIPH_GPIOE, ENABLE);
	
	
	n32_msp_sdio_init((void *)(n32_sdio->Instance));
	
	/*!< Enable the SDIO AHB Clock */
	RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_SDIO, ENABLE);

	/*!< Enable the DMA2 Clock */
	RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_DMA2, ENABLE);
	
	NVIC_InitType NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel = SDIO_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
	NVIC_Init(&NVIC_InitStruct);
	
	n32_sdio->SDIO_InitStructure.ClkDiv = n32_get_clk_div(400 * 1000); // SDIO_INIT_CLK_DIV;
	n32_sdio->SDIO_InitStructure.ClkEdge = SDIO_CLKEDGE_RISING;
	n32_sdio->SDIO_InitStructure.ClkBypass = SDIO_ClkBYPASS_DISABLE; // Do not use bypass mode, directly use hclk to divide the frequency to get SDIO "CK"
	n32_sdio->SDIO_InitStructure.ClkPwrSave = SDIO_CLKPOWERSAVE_DISABLE;          // Do not turn off clock power when idle
	n32_sdio->SDIO_InitStructure.BusWidth = SDIO_BUSWIDTH_1B;                     // 1-bit data line
	n32_sdio->SDIO_InitStructure.HardwareClkCtrl = SDIO_HARDWARE_CLKCTRL_DISABLE; // Do not enable hardware flow
	SDIO_Init(&(n32_sdio->SDIO_InitStructure));

	/*!< Set Power State to ON */
	SDIO_SetPower(SDIO_POWER_CTRL_ON);

	/*!< Enable SDIO Clock */
	SDIO_EnableClock(ENABLE);
}


int rt_hw_sdio_init(void)
{
	n32_sdio1.Instance = SDIO;
	
	n32_sdio_hw_init(&n32_sdio1); //init sdio hw gpio/clk/irq
	
	n32_sdio1.host =  n32_sdio_host_create(&n32_sdio1); //create rtthread sdio host

	n32_sdio_ops_enable_sdio_irq(n32_sdio1.host, 1);  //enable irq

	/* ready to change */
	mmcsd_change(n32_sdio1.host);
	return 0;
}
// INIT_DEVICE_EXPORT(rt_hw_sdio_init);

//void sdio_inti_cmd(uint8_t argc, char **argv)
//{
//	rt_kprintf("sdio init\n");
//	rt_hw_sdio_init();
//}
//MSH_CMD_EXPORT(sdio_inti_cmd, sdio_inti_cmd);


void n32g45x_mmcsd_change(void)
{
    mmcsd_change(n32_sdio1.host);
}

#endif
